﻿<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>OnError - 语法 &amp; 使用 | AutoHotkey v2</title>
<meta name="description" content="The OnError function registers a function to be called automatically whenever an unhandled error occurs." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="../static/theme.css" rel="stylesheet" type="text/css" />
<script src="../static/content.js" type="text/javascript"></script>
</head>
<body>

<h1>OnError</h1>
<p>注册一个当发生未处理错误时, 会自动调用的<a href="../Functions.htm">函数</a>.</p>

<pre class="Syntax"><span class="func">OnError</span> Callback <span class="optional">, AddRemove</span></pre>

<h2 id="Parameters">参数</h2>
<dl>

  <dt>Callback</dt>
  <dd>
    <p>类型: <a href="../misc/Functor.htm">函数对象</a></p>
    <p>要调用的函数.</p>
    <p>回调接受两个参数,  可以<a href="../Functions.htm#intro">定义</a>如下:</p>
    <pre class="NoIndent">MyCallback(Thrown, Mode) { ...</pre>
    <p>虽然你给参数的名称并不重要, 但是下面的值会依次赋值给它们:</p>
    <ol>
      <li>抛出的值, 通常为 <a href="Error.htm">Error 对象</a>.</li>
      <li>错误的模式: Return, Exit 或 ExitApp. 有关详情, 请参阅<a href="#Error_Modes">下面的表格</a>.</li>
    </ol>
    <p>如果不需要相应的信息, 可以从回调参数列表的末尾省略一个或多个参数, 但在这种情况下, 必须指定星号作为最后一个参数, 例如 <code>MyCallback(Param1, *)</code>.</p>
    <p>回调可以返回以下值之一(其他值是为将来使用而保留的, 应该避免):</p>
    <ul>
      <li><code>0</code>, <code>""</code> 或没有 Return: 允许错误处理正常进行.</li>
      <li><code>1</code>: 抑制默认错误对话框和任何剩余的错误回调.</li>
      <li><code>-1</code>: 同上, 但如果 <em>Mode</em>(第二个参数) 包含单词 Return, 则允许继续执行当前线程.</li>
    </ul>
  </dd>
  
  <dt>AddRemove</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#numbers">整数</a></p>
    <p>如果省略, 则默认为 1. 否则, 指定下列数字之一:</p>
    <ul>
      <li>1 = 在所有之前注册的回调之后调用该回调.</li>
      <li>-1 = 在所有之前注册的回调之前调用该回调.</li>
      <li>0 = 不调用该回调.</li>
    </ul>
  </dd>

</dl>

<h2 id="Error_Modes">Error 模式</h2>
<table class="info">
  <tr>
    <th>模式</th>
    <th abbr="Descr">描述</th>
  </tr>
  <tr>
    <td>Return</td>
    <td>抛出的值是一个可继续的运行时错误. 如果回调返回 -1, 线程继续; 否则线程退出.</td>
  </tr>
  <tr>
    <td>Exit</td>
    <td>抛出的值是一个不可继续的运行时错误或由脚本 <a href="Throw.htm">thrown</a> 的值. 线程将退出.</td>
  </tr>
  <tr>
    <td>ExitApp</td>
    <td>抛出的值是一个严重的运行时错误, 例如由 DllCall 检测到的损坏. 程序将退出.</td>
  </tr>
</table>

<h2 id="Remarks">备注</h2>
<p><em>Callback</em> 只会被调用来处理通常会导致显示错误消息的错误或异常. 加载时的错误无法调用, 因为在加载脚本完毕之前无法调用 OnError.</p>
<p><em>Callback</em> 在当前<a href="../misc/Threads.htm">线程</a>中被调用, 在线程退出之前(即在调用堆栈展开之前).</p>

<h2 id="Related">相关</h2>
<p><a href="Try.htm">Try</a>, <a href="Catch.htm">Catch</a>, <a href="Throw.htm">Throw</a>, <a href="OnExit.htm">OnExit</a></p>

<h2 id="Examples">示例</h2>
<div class="ex" id="ExBasic">
<p><a class="ex_number" href="#ExBasic"></a> 将脚本产生的错误记录到文本文件, 而不是显示给用户.</p>
<pre>
OnError LogError
i := Integer("cause_error")

LogError(exception, mode) {
    FileAppend "Error on line " exception.Line ": " exception.Message "`n"
        , "errorlog.txt"
    return true
}</pre>
</div>

<div class="ex" id="ExAccumulator">
<p><a class="ex_number" href="#ExAccumulator"></a> 使用 OnError 来实现其他错误处理方法. 注意: 当 <a href="Try.htm">Try</a> 处于活动状态时, OnError 是无效的.</p>
<pre>
AccumulateErrors()
{
    local ea := ErrorAccumulator()
    ea.Start()
    return ea
}

class ErrorAccumulator
{
    Errors := []                        <em>; 累计错误数组.</em>
    _cb := AccumulateError.Bind(this.Errors)
    Start() => OnError(this._cb, -1)    <em>; 先注册 cb.</em>
    Stop() => OnError(this._cb, 0)      <em>; 取消注册 cb.</em>
    Last => this.Errors[-1]             <em>; 最近的错误.</em>
    Count => this.Errors.Length         <em>; 累计错误的数目.</em>
    __item[i] => this.Errors[i]         <em>; 索引.</em>
    __delete() => this.Stop()           <em>; 用于与函数范围绑定.</em>
}

<em>; 这是 OnError 回调. 'errors' 是通过 Bind() 给定的值.</em>
AccumulateError(errors, e, mode)
{
    if mode != "Return" <em>; 不可继续.</em>
        return
    if e.What = "" <em>; 表达式缺陷或类似的, 不是内置函数.</em>
        return
    try {
        <em>; 尝试将错误打印到 stdout.</em>
        FileAppend Format("{1} ({2}) : ({3}) {4}`n", e.File, e.Line, e.What, e.Message), "*"
        if HasProp(e, "extra")
            FileAppend "     Specifically: " e.Extra "`n", "*"
    }
    errors.Push(e)
    return -1 <em>; 继续.</em>
}

RearrangeWindows()
{
    <em>; 开始在 'err' 中累积错误.</em>
    local err := AccumulateErrors()

    <em>; 做一些可能失败的事情...</em>
    MonitorGetWorkArea, &amp;left, &amp;top, &amp;right, &amp;bottom
    width := (right-left)//2, height := bottom-top
    WinMove left, top, width, height, A_ScriptFullPath
    WinMove left+width, top, width, height, "AutoHotkey v2 Help"

    <em>; 检查是否发生任何错误.</em>
    if err.Count
        MsgBox err.Count " error(s); last error at line #" err.Last.Line
    else
        MsgBox "No errors"

    <em>; 当变量超出作用域时, Stop 会被自动调用,
    ; 因为只有我们有一个对象的引用. 这将导致 OnError
    ; 被调用来解除回调.
    ; err.Stop()</em>
}

<em>; 调用抑制和累积错误的测试函数.</em>
RearrangeWindows()
<em>; 调用另一个函数来显示恢复正常的错误行为.</em>
WinMove 0, 0, 0, 0, "non-existent window"
</pre>
</div>

</body>
</html>